Chojecki Przemysław
import numpy as np
import pandas as pd
import dalex as dx
import pickle
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
dataset = pd.read_csv('new_preprocessed_dataset.csv')
y = dataset.loc[:,'Attrition']
X = dataset.drop('Attrition', axis='columns')
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=14)
X_train.head()
| Gender | Missing_Income | Income_Category | Missing_Education | Education_Level | Card_Category | Marital_Divorced | Marital_Married | Marital_Single | Marital_Unknown | ... | Avg_Open_To_Buy | Total_Trans_Amt | Dependent_count | Total_Relationship_Count | Months_Inactive_12_mon | Contacts_Count_12_mon | Total_Revolving_Bal | Total_Amt_Chng_Q4_Q1 | Total_Trans_Ct | Total_Ct_Chng_Q4_Q1 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 8201 | 0 | 0 | 1 | 0 | 4 | 1 | 0 | 0 | 1 | 0 | ... | 1678.0 | 2475 | 3 | 1 | 3 | 4 | 0 | 0.676 | 61 | 0.694 |
| 9163 | 1 | 0 | 5 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | ... | 34516.0 | 5603 | 3 | 1 | 2 | 3 | 0 | 0.377 | 56 | 0.244 |
| 8127 | 0 | 0 | 2 | 0 | 3 | 1 | 0 | 1 | 0 | 0 | ... | 799.0 | 4894 | 2 | 2 | 1 | 3 | 642 | 0.563 | 84 | 0.826 |
| 9146 | 1 | 0 | 3 | 0 | 2 | 1 | 0 | 0 | 1 | 0 | ... | 4259.0 | 13630 | 0 | 1 | 1 | 1 | 1965 | 0.739 | 124 | 0.699 |
| 3394 | 0 | 1 | 1 | 0 | 4 | 1 | 0 | 0 | 1 | 0 | ... | 16800.0 | 3523 | 3 | 4 | 3 | 4 | 545 | 0.905 | 64 | 0.829 |
5 rows × 24 columns
model_rf = pickle.load(open('new_random_forest_model.p', 'rb'))
explainer = dx.Explainer(model_rf, X_train, y_train)
Preparation of a new explainer is initiated -> data : 6785 rows 24 cols -> target variable : Parameter 'y' was a pandas.Series. Converted to a numpy.ndarray. -> target variable : 6785 values -> model_class : sklearn.ensemble._forest.RandomForestClassifier (default) -> label : Not specified, model's class short name will be used. (default) -> predict function : <function yhat_proba_default at 0x7ff27e090940> will be used (default) -> predict function : Accepts pandas.DataFrame and numpy.ndarray. -> predicted values : min = 0.0, mean = 0.157, max = 1.0 -> model type : classification will be used (default) -> residual function : difference between y and yhat (default) -> residuals : min = -0.32, mean = 0.000721, max = 0.42 -> model_info : package sklearn A new explainer has been created!
obserwacja = 17
print(explainer.predict(X_test)[obserwacja])
print(y_test.iloc[obserwacja])
0.53 1
Dla obserwacji nr $17$ model przewiduje, że na $53\%$ będzie wartość $1$. Okazuje się to poprawną predykcją - ten klient opuścił bank, ale jak widać model był całkiem niepewny. Przyjrzyjmy się, czemu model w tym przypadku miał trudności.
cp = explainer.predict_profile(X_test.iloc[obserwacja,:])
cp.plot()
Calculating ceteris paribus: 100%|██████████| 24/24 [00:00<00:00, 27.16it/s]
Wiele z komumn w modelu są 0-1-kowe, a wykresy tych kolumn nudne.
Każdy wykres pokazuje jak zmienia się predykcja ustalając na stałe pozostałe wartości, a manipulując tylko jedną.
Większość informacji wyciąganych z modelu wydaje się logiczna, na przykład:
Było jednak kilka zaskoczeń:
obserwacja = 117
cp = explainer.predict_profile(X_test.iloc[obserwacja,:])
cp.plot()
Calculating ceteris paribus: 100%|██████████| 24/24 [00:01<00:00, 15.11it/s]
Wciąż widać, że wartości Total_Trans_Amt w przedziale $\$1k - \$5k$ są "złotym miejscem" dla klientów, którzy nie opuszczą raczej naszego banku. Tym razem jednak ta różnica nie jest aż tak widoczna jak wcześniej. Zapewne inne czynnkiki są "niepewne", dlatego ta wartość ma bardzo duży wpływ.
W przypdaku tej obserwacji nieaktywność klientów przez więcej niż $2$ miesiące niema aż tak dużego wpływu na przewidywania co w przypadku poprzedniej obserwacji.